/*
 *  MacEnterprise802.1X.m
 *  MacEnterprise802.1X
 *
 *  Created by Karl J. Kuehn on 7/25/06.
 *  Copyright 2006-2007 Karl J. Kuehn. All rights reserved.
 *	larkost@gmail.com larkost@softhome.net
 *
 *
 *	License: Modified-BSD
 *
 *	Redistribution and use in source and binary forms, with or without
 *	modification, are permitted provided that the following conditions are met:
 *		*	Redistributions of source code must retain the above copyright
 *			notice, this list of conditions and the following disclaimer.
 *		*	Redistributions in binary form must reproduce the above copyright
 *			notice, this list of conditions and the following disclaimer in the
 *			documentation and/or other materials provided with the distribution.
 *		*	Neither the name of the Karl J. Kuehn nor the
 *			names of its contributors may be used to endorse or promote products
 *			derived from this software without specific prior written permission.
 *
 *	THIS SOFTWARE IS PROVIDED BY Karl J. Kuehn ``AS IS'' AND ANY
 *	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *	DISCLAIMED. IN NO EVENT SHALL Karl J. Kuehn BE LIABLE FOR ANY
 *	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	Modified on 6/19/07		- reworked to fix memory bug
 *	Modified on 8/22/07		- based everything on the NSDictionary (now an array)
 *	Modified on 8/24/07		- more rework to put in the latest Certificate, and the whole InternetConnect section added
 *	Modified on 8/26/07		- added fix for 10.5 (beta) bug, added license
 *	Modified on 8/27/07		- EAP settings files for new build of 10.5
 *	Modified on 9/4/07		- Checking certificate trust settings before installing and allowing for sudo install
 *	Modified on 9/6/07		- Added DirectoryServices calls to get the users when not supplied
 *
 *	Modified on 10/29/07	- Finally put under proper copywrite and license, removed Penn atributions
 */
 
/*

 The certificates that this should do its work on, and the InternetConnect settings are hard coded into this program.
	Look for the "// SETTING HERE" markers to signal where changes should be made
 
 In order to get the certificates formated properly you will probably want to export it from the Keychain in cer format and then use hexdump:
	hexdump -v -e '"\t" 25/1 "0x%02X, " "\n"' <path to file>
 Remember to cut off any excess at the end
 
 TODO list:
	- rationalize the error messages and reporting method
	- integrate trust settings instead of stomping on them
	- cleanup the DS section and take care of the memory leaks there

*/
 
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>

// we are going to use this to figure out what OS we are on, and so what preference files we are allowed to fail on
#include <sys/utsname.h>

// to handle when this is run as root
#include <unistd.h>

// to get ahold of users list
#import <DirectoryService/DirectoryService.h>

// and this is for the 10.5 bug... since we can't depend on headers on pre-10.5:
#define kIO80211InterfaceClass "IO80211Interface"

// these are to pass whether an error is only fotal for Leopard, pre-Leopard, neither, or both
enum errorTypes {
	fatalForLeopard,
	fatalForPreLeopard,
	fatalForBoth,
	fatalForNeither
};

// this might go away at some point
#define MAX_USERS 40

// I hate it, but I do need a single global variable
BOOL isLeopardOrLater = FALSE;

// this is how we will pass arround users
struct userData {
	NSString * name;
	NSString * homeDirectory;
	uid_t uid;
};

// this is for passing errors back and forth about users
enum userErrorCodes {
	udNoError,
	udErrorStructNotPassed,
	udErrorBadCharactersInUserName,
	udErrorNoHomeDirectory,
	udErrorNoUIDForUser
};

// this sets-up and validates the user
enum userErrorCodes setupUserData(NSString * userNSName, struct userData * destination) {
	
	if (destination == NULL) {
		return 1;
	}
	
	if (userNSName == nil) {
		return 2;
	}
	
	char * userName = (char *)[userNSName cString];
	
	// first we need to validate the user name
	char acceptableCharacters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
	// probably should be a hash, but we are not all that long
	NSMutableString * resultString = [NSMutableString stringWithString:@""]; // an endian issue forces this one
	for (int i = 0; i < strlen(userName); i++) {
		for (int j = 0; j < strlen(acceptableCharacters); j++) {
			if (userName[i] == acceptableCharacters[j]) {
				
				[resultString appendString:[NSString stringWithCString:&acceptableCharacters[j] length:1]];
			}
		}
	}
	if ([resultString isEqualToString:[NSString stringWithCString:userName]] == NO) {
		return 2;
	}
	
	NSString * userHomeDirectory = NSHomeDirectoryForUser(resultString);
	if (userHomeDirectory == nil) {
		return 3;
	}
	
	NSDictionary * attributes = [[NSFileManager defaultManager] fileAttributesAtPath:userHomeDirectory traverseLink: YES];
	NSNumber * userID = [attributes objectForKey: NSFileOwnerAccountID];
	if (userID == nil) {
		return  4;
	}
	
	// at this point we have all of the data, so we can put it in
	destination->name = [resultString retain];
	destination->homeDirectory = [userHomeDirectory retain];
	destination->uid = (uid_t)[userID intValue];
	
	return 0;
}

// this bit is inspired by Keychain.framework, but I like it inline
#define isCSSMOIDtheSame(a, b) (a.Length == b.Length && memcmp(a.Data, b.Data, a.Length) == 0) ? TRUE : FALSE 

// this will make life easier
#define ERROR(message, number, fatalFor) if (reactToError(message, number, fatalFor) != 0) { return number; }
// this is to standardize out error and warning reporting
int reactToError (NSString * errorText, int errorNumber, enum errorTypes isFatal ) {
	if (isFatal == fatalForBoth || (isLeopardOrLater && isFatal == fatalForLeopard) || (!isLeopardOrLater && isFatal == fatalForPreLeopard)) {
		// this is a fatal error
		printf("Error: %s (error number: %d)\n", [errorText cString], errorNumber);
		return errorNumber;
		
	} else {
		// non-fatal errror
		printf("Warning: %s (error number: %d)\n", [errorText cString], errorNumber);
		return 0;
	}
}

BOOL getDateAndNameOfCertificate ( SecCertificateRef theCertificate, NSCalendarDate ** thisDate, NSString ** thisName) {
	
	NSCalendarDate * tempDate;
	NSString *tempName;
	
	CSSM_DATA myCSSMData;
	if (noErr != SecCertificateGetData(theCertificate, &myCSSMData)) { return FALSE; }
	
	uint32 numberOfFields = 0;
	CSSM_CL_HANDLE clHandle;
	CSSM_FIELD_PTR fields;
	if (noErr == SecCertificateGetCLHandle(theCertificate, &clHandle)) {

		if (CSSM_OK == CSSM_CL_CertGetAllFields(clHandle, &myCSSMData, &numberOfFields, &fields)) {

			for (int j = 0; j < numberOfFields; ++j) {
				
				if (isCSSMOIDtheSame(fields[j].FieldOid, CSSMOID_X509V1ValidityNotAfter)) {
					// here we are working on the end date
					
					CSSM_X509_TIME_PTR thisTimePtr = (CSSM_X509_TIME_PTR)fields[j].FieldValue.Data;
					
					if (thisTimePtr->timeType == BER_TAG_UTC_TIME) {
						
						NSString * myTimeString = [NSString stringWithCString:(const char *)thisTimePtr->time.Data length:(thisTimePtr->time.Length -1)];
						// here I am whiping out the time zone information, this has a very small chance of messing us up
						if (myTimeString == nil) { return nil; }
							
						tempDate = [NSCalendarDate dateWithString:myTimeString calendarFormat:@"%y%m%d%H%M%S"];
						
					} else if (thisTimePtr->timeType == BER_TAG_GENERALIZED_TIME) {
						NSString * myTimeString = [NSString stringWithCString:(const char *)thisTimePtr->time.Data length:(thisTimePtr->time.Length -1)];
						// here I am whiping out the time zone information, this has a very small chance of messing us up
						if (myTimeString == nil) { return nil; }
							
						tempDate = [NSCalendarDate dateWithString:myTimeString calendarFormat:@"%Y%m%d%H%M%S"];
					}
					
				} else if (isCSSMOIDtheSame(fields[j].FieldOid, CSSMOID_X509V1SubjectNameCStruct)) {
					// here we are working on the name of the item.. this is rediculously complex...
					
					CSSM_X509_NAME_PTR x509NamePtr = (CSSM_X509_NAME_PTR)fields[j].FieldValue.Data;
					
					for (int k = 0; k < x509NamePtr->numberOfRDNs; k++) {
						CSSM_X509_RDN_PTR relDistNamePtr = &x509NamePtr->RelativeDistinguishedName[k];
						
						for (int l = 0; l < relDistNamePtr->numberOfPairs; l++) {
							CSSM_X509_TYPE_VALUE_PAIR * thisAttributeTypeAndValue = &relDistNamePtr->AttributeTypeAndValue[l];
							
							if(isCSSMOIDtheSame(thisAttributeTypeAndValue->type, CSSMOID_CommonName)) {
								tempName = [NSString stringWithCString:(char *)thisAttributeTypeAndValue->value.Data length:thisAttributeTypeAndValue->value.Length];
							}
						}
					}
				}
			}
		}
	}

	if (tempDate == nil || tempName == nil) {
		return FALSE;
	}
	if (thisName == NULL || thisName == nil) {
		thisName = nil;
	} else {
		*thisName = tempName;
	}
	*thisDate = tempDate;
	[tempName retain];
	[tempDate retain];
	
	return TRUE;
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	NSFileManager * myFileManager = [NSFileManager defaultManager];

#pragma mark Figure out if we are running on Leopard
	
	struct utsname systemInformation;
	if (uname(&systemInformation) == 0) {
		//printf("Sysname: %s\nRelease: %s\nVersion: %s\nMachine: %s\n\n", systemInformation.sysname, systemInformation.release, systemInformation.version, systemInformation.machine);
		
		// now that we have the kernal version, lets take it apart and see if this is 10.5.0 or newer
		
		int majorVersion = [[[[NSString stringWithCString:systemInformation.release] componentsSeparatedByString:@"."] objectAtIndex:0] intValue];
		//int minorVersion = [[[[NSString stringWithCString:systemInformation.release] componentsSeparatedByString:@"."] objectAtIndex:1] intValue];
		//printf("The version is %d and %d\n", majorVersion, minorVersion);
		
		
		if (majorVersion > 8) { // need to figure this out
			isLeopardOrLater = TRUE;
		}

		//componentsSeparatedByString:@"."
		
	} else {
		ERROR(@"Was not able to get system information!", 1, fatalForBoth)
	}
	
#pragma mark Get usename, uid, and path to home directory

	struct userData theUsers[MAX_USERS]; // this should have a number here
	int numberOfUsers = 0;
	
	enum userErrorCodes thisErrorCode;
	
	if (argc == 1) {
		if (0 == getuid()) {
			// root, without arguments
			
#pragma mark - from DirectoryServices			
			
			tDirReference myDirectoryConnection;
			if (eDSNoErr != dsOpenDirService (&myDirectoryConnection)) {
				ERROR(@"Unable to open DirectoryServices!", 1, fatalForBoth)
			}
			
			tDataListPtr usersList;
			if (isLeopardOrLater == TRUE) { usersList = dsBuildFromPath (myDirectoryConnection, "Local/Default", "/"); }
			else { usersList = dsBuildFromPath (myDirectoryConnection, "NetInfo/root", "/"); }
			
			if (usersList == NULL) { ERROR(@"DirectoryServices error number 1!", 1, fatalForBoth) }
			
			tDirNodeReference usersNode;
			tDirStatus mystatus =  dsOpenDirNode (
												  myDirectoryConnection, 
												  usersList, 
												  &usersNode
												  );
			
			tDataBufferPtr thisUserGroup = dsDataBufferAllocate (myDirectoryConnection, 10 * 1024);
			UInt32 retrievedCount;
			tContextData context = NULL;
			
			tDataList recNames;
			tDataList recTypes;
			tDataList attrTypes;
			mystatus = dsBuildListFromStringsAlloc ( myDirectoryConnection, &recNames,  kDSRecordsAll, NULL );
			mystatus = dsBuildListFromStringsAlloc ( myDirectoryConnection, &recTypes,  kDSStdRecordTypeUsers, NULL );
			mystatus = dsBuildListFromStringsAlloc ( myDirectoryConnection, &attrTypes,  kDSAttributesAll, NULL );
			do {
				mystatus =  dsGetRecordList (
											 usersNode,
											 thisUserGroup,
											 &recNames,
											 eDSAnyMatch, // we only want local people
											 &recTypes,
											 &attrTypes,
											 false, // not sure about this one
											 &retrievedCount,
											 &context
											 );
				
				for (UInt32 personNumber = 1 ; personNumber <= retrievedCount; personNumber++) {
					
					tAttributeListRef attrListRef;
					tRecordEntry * pRecEntry = NULL;
					
					mystatus = dsGetRecordEntry (
												 usersNode,
												 thisUserGroup,
												 personNumber,
												 &attrListRef,
												 &pRecEntry
												 );
					
					tAttributeEntry * pAttrEntry;
					tAttributeValueListRef valueRef;
					
					NSMutableDictionary * attributesWeWant = 
						[NSMutableDictionary dictionaryWithObjectsAndKeys: @"", @"dsAttrTypeStandard:RecordName", @"", @"dsAttrTypeStandard:UniqueID", @"", @"dsAttrTypeStandard:NFSHomeDirectory", nil];
					
					for (UInt32 attributeNumber = 1; attributeNumber < pRecEntry->fRecordAttributeCount; attributeNumber++) {
						mystatus = dsGetAttributeEntry( usersNode, thisUserGroup,  attrListRef, attributeNumber, &valueRef, &pAttrEntry );
						
						NSString * attributeName = nil;
						if (pAttrEntry->fAttributeSignature.fBufferLength != 0) {
							attributeName = [NSString stringWithCString:pAttrEntry->fAttributeSignature.fBufferData length:pAttrEntry->fAttributeSignature.fBufferLength];
							//					printf("attributeSig: %s \t", [attributeName cString]);
						}
						

						
						if (attributeName != nil && [attributesWeWant valueForKey:attributeName] != nil) {
							for (int k = 1; k <= pAttrEntry->fAttributeValueCount;  k++ ) {
								tAttributeValueEntry *pValueEntry;
								
								mystatus = dsGetAttributeValue( usersNode,  thisUserGroup, k,  valueRef, &pValueEntry );
								
								[attributesWeWant
							setObject:[NSString stringWithCString:pValueEntry->fAttributeValueData.fBufferData length:pValueEntry->fAttributeValueData.fBufferLength]
							   forKey:attributeName
									];
								
								dsDeallocAttributeValueEntry(myDirectoryConnection,  pValueEntry);
							}
						}
					}
					if (
						![[attributesWeWant objectForKey:@"dsAttrTypeStandard:RecordName"] isEqualToString:@""] &&
						![[attributesWeWant objectForKey:@"dsAttrTypeStandard:UniqueID"] isEqualToString:@""] &&
						![[attributesWeWant objectForKey:@"dsAttrTypeStandard:NFSHomeDirectory"] isEqualToString:@""] &&
						[[attributesWeWant objectForKey:@"dsAttrTypeStandard:UniqueID"] intValue] >= 500 // best guess to seperate the sheep from the goats
						) {
						struct userData temp;
						temp.name = [[attributesWeWant objectForKey:@"dsAttrTypeStandard:RecordName"] retain];
						temp.homeDirectory = [[attributesWeWant objectForKey:@"dsAttrTypeStandard:NFSHomeDirectory"] retain];
						temp.uid = (uid_t)[[attributesWeWant objectForKey:@"dsAttrTypeStandard:UniqueID"] intValue];
						
						theUsers[numberOfUsers] = temp;
						numberOfUsers++;
					}
					
					
					dsDeallocRecordEntry(myDirectoryConnection, pRecEntry);
					dsCloseAttributeList(attrListRef); // not much point in error checking this
					
				}
				
				
				
				
			} while (context != NULL && retrievedCount > 0);
			
			// http://developer.apple.com/documentation/Networking/Conceptual/Open_Directory/workingWithRecords/chapter_5_section_2.html
			
			//dsDataListDeallocate(usersList);
			//dsDataBufferDeAllocate
			dsCloseDirService(myDirectoryConnection);

			
		} else {
			// normal user, just them

#pragma mark - from the user that launched us			

			struct userData temp;
			thisErrorCode = setupUserData(NSUserName(), &temp);
			if (thisErrorCode == udNoError) {
				theUsers[numberOfUsers] = temp;
				numberOfUsers = 1;
			} else {
				printf("we should fail here\n");
			}
			
		}
	} else {
		if (0 == getuid()) {
			// root, and they have specified the users

#pragma mark - from the command-line supplied user list
			
			for (int k = 1; k < argc; k++) {
				struct userData temp;
				thisErrorCode = setupUserData([NSString stringWithCString:argv[k]], &temp);
				if (thisErrorCode == udNoError) {
					theUsers[numberOfUsers] = temp;
//printf("testing: %d\n", numberOfUsers);
//printf("second: %s\n", theUsers[].name
					numberOfUsers++;
				} else {
					printf("we should fail here\n");
				}
			}
			
		} else {
			// they have specified users, but they are not root
			ERROR(@"If you are going to supply users you must run as root!", 1, fatalForBoth)
		}
	}

#pragma mark Certificate Data
	
	// and here are the certificates
	//	there should only be one certificate per encoded blob, otherwise the trust setting will not get set
	//	don't forget to put the references in below
	
	char noc2CertificateRAW[] = { // SETTING HERE
        0x30, 0x82, 0x03, 0x9C, 0x30, 0x82, 0x03, 0x05, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x6C, 0x08, 0x1A, 0x28, 0x3B, 0x2C, 0x1C, 0xEB, 0x64, 0xFA,
        0x51, 0x1A, 0x70, 0x9D, 0xDE, 0x4E, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0xCE, 0x31,
        0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x5A, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0C, 0x57, 0x65,
        0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x43, 0x61, 0x70, 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x61, 0x70, 0x65,
        0x20, 0x54, 0x6F, 0x77, 0x6E, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x43, 0x6F,
        0x6E, 0x73, 0x75, 0x6C, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x1F, 0x43, 0x65, 0x72,
        0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73,
        0x69, 0x6F, 0x6E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6D,
        0x69, 0x75, 0x6D, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30, 0x26, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
        0x01, 0x09, 0x01, 0x16, 0x19, 0x70, 0x72, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x2D, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74,
        0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x30, 0x37, 0x30, 0x32, 0x32, 0x32, 0x32, 0x30, 0x33, 0x38, 0x34, 0x38, 0x5A, 0x17, 0x0D, 0x30,
        0x38, 0x30, 0x33, 0x31, 0x35, 0x32, 0x31, 0x32, 0x37, 0x35, 0x33, 0x5A, 0x30, 0x81, 0xA0, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
        0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0C, 0x50, 0x65, 0x6E, 0x6E, 0x73, 0x79, 0x6C, 0x76, 0x61, 0x6E, 0x69,
        0x61, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0C, 0x50, 0x68, 0x69, 0x6C, 0x61, 0x64, 0x65, 0x6C, 0x70, 0x68, 0x69, 0x61, 0x31,
        0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1A, 0x55, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x69, 0x74, 0x79, 0x20, 0x6F, 0x66, 0x20, 0x50,
        0x65, 0x6E, 0x6E, 0x73, 0x79, 0x6C, 0x76, 0x61, 0x6E, 0x69, 0x61, 0x31, 0x20, 0x30, 0x1E, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x17, 0x49, 0x53, 0x43,
        0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x65, 0x72, 0x69, 0x6E, 0x67, 0x31, 0x1C, 0x30, 0x1A, 0x06,
        0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x6E, 0x6F, 0x63, 0x32, 0x2E, 0x64, 0x63, 0x63, 0x73, 0x2E, 0x75, 0x70, 0x65, 0x6E, 0x6E, 0x2E, 0x65, 0x64, 0x75,
        0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89,
        0x02, 0x81, 0x81, 0x00, 0xC2, 0x5B, 0xEF, 0xA1, 0x43, 0x95, 0x58, 0x43, 0x4F, 0x51, 0xBC, 0x2D, 0x19, 0x0F, 0x43, 0x3C, 0x2F, 0xC9, 0xFE, 0x65, 0x63,
        0xAB, 0x4A, 0xB2, 0x6D, 0xC0, 0x85, 0xE0, 0x16, 0xFA, 0x68, 0x37, 0x56, 0xA5, 0x32, 0x9F, 0x41, 0xFF, 0xDF, 0x61, 0xB8, 0xA7, 0xDA, 0xAC, 0x05, 0x30,
        0x47, 0xC9, 0x9B, 0x70, 0xB9, 0x11, 0xA9, 0x7B, 0xE3, 0x13, 0x9B, 0x51, 0x82, 0x27, 0x6D, 0x92, 0x81, 0x4D, 0xFB, 0xD3, 0xBA, 0xF8, 0xEC, 0x47, 0xA7,
        0x77, 0x24, 0x08, 0x59, 0x81, 0x69, 0x17, 0xAF, 0x55, 0x21, 0xF0, 0x93, 0x4C, 0xB9, 0x64, 0xD8, 0xDE, 0x5E, 0xDA, 0xA6, 0x4B, 0x91, 0x16, 0x36, 0xBF,
        0x95, 0x53, 0xA1, 0x8A, 0x46, 0x52, 0x46, 0xA3, 0xA1, 0x19, 0x12, 0x87, 0x50, 0x83, 0xC9, 0x01, 0xB0, 0xDB, 0x13, 0xA2, 0xAB, 0x73, 0x23, 0x6F, 0xF9,
        0xCA, 0xBC, 0xA7, 0x8A, 0xE2, 0x2D, 0xE5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x81, 0xA6, 0x30, 0x81, 0xA3, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x25,
        0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
        0x40, 0x06, 0x03, 0x55, 0x1D, 0x1F, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xA0, 0x33, 0xA0, 0x31, 0x86, 0x2F, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
        0x63, 0x72, 0x6C, 0x2E, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6D,
        0x69, 0x75, 0x6D, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2E, 0x63, 0x72, 0x6C, 0x30, 0x32, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07,
        0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3A,
        0x2F, 0x2F, 0x6F, 0x63, 0x73, 0x70, 0x2E, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01,
        0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
        0xBA, 0x4E, 0x2E, 0x25, 0x45, 0xA6, 0x9D, 0x40, 0xC2, 0x21, 0x24, 0x0E, 0xE8, 0x54, 0xD6, 0x00, 0xA1, 0x9C, 0x62, 0xA6, 0x95, 0xCB, 0xDE, 0x9E, 0xB2,
        0x15, 0xDC, 0xB1, 0xBC, 0x54, 0xFF, 0xAC, 0xBB, 0x37, 0xEF, 0x67, 0xBC, 0x53, 0xA2, 0xDE, 0xC7, 0x17, 0x4E, 0xC7, 0xE2, 0xD7, 0x7B, 0xAD, 0xCD, 0xBB,
        0x33, 0x74, 0x02, 0xF8, 0xCE, 0xDD, 0x91, 0x91, 0x1B, 0x30, 0x3B, 0x1E, 0xC2, 0x71, 0x39, 0xDA, 0x1A, 0xD1, 0xB4, 0xBA, 0xBF, 0x52, 0xE6, 0xF4, 0x49,
        0x37, 0xE3, 0xA4, 0x04, 0xF6, 0x19, 0xAF, 0x0B, 0xFB, 0x78, 0x64, 0xE1, 0xC2, 0x14, 0x2F, 0x11, 0x98, 0x3A, 0x4B, 0xCB, 0x70, 0x89, 0x63, 0xCD, 0x47,
        0x79, 0x41, 0xE6, 0xDB, 0xE5, 0x8A, 0x97, 0x85, 0xE4, 0x70, 0x3A, 0x4F, 0x52, 0x55, 0x65, 0x7E, 0xC8, 0x2C, 0x4A, 0xA5, 0xE5, 0x07, 0xEC, 0x43, 0xF2,
        0xA9, 0x43, 0xEB
	};
	
	char noc4CertificateRAW[] = { // SETTING HERE
		0x30, 0x82, 0x03, 0x93, 0x30, 0x82, 0x02, 0xFC, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x54, 0x87, 0xD6, 0x15, 0x37, 0xBC, 0x18, 0xD6, 0xBE, 0x72,
        0x4B, 0x5C, 0x17, 0x36, 0x1A, 0x0F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0xCE, 0x31,
        0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x5A, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0C, 0x57, 0x65,
        0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x43, 0x61, 0x70, 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x61, 0x70, 0x65,
        0x20, 0x54, 0x6F, 0x77, 0x6E, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x43, 0x6F,
        0x6E, 0x73, 0x75, 0x6C, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x1F, 0x43, 0x65, 0x72,
        0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73,
        0x69, 0x6F, 0x6E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6D,
        0x69, 0x75, 0x6D, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30, 0x26, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
        0x01, 0x09, 0x01, 0x16, 0x19, 0x70, 0x72, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x2D, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74,
        0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x30, 0x36, 0x31, 0x30, 0x31, 0x31, 0x31, 0x37, 0x35, 0x35, 0x30, 0x34, 0x5A, 0x17, 0x0D, 0x30,
        0x37, 0x31, 0x30, 0x31, 0x31, 0x31, 0x37, 0x35, 0x35, 0x30, 0x34, 0x5A, 0x30, 0x81, 0x97, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
        0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0C, 0x50, 0x65, 0x6E, 0x6E, 0x73, 0x79, 0x6C, 0x76, 0x61, 0x6E, 0x69,
        0x61, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0C, 0x50, 0x68, 0x69, 0x6C, 0x61, 0x64, 0x65, 0x6C, 0x70, 0x68, 0x69, 0x61, 0x31,
        0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1A, 0x55, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x69, 0x74, 0x79, 0x20, 0x6F, 0x66, 0x20, 0x50,
        0x65, 0x6E, 0x6E, 0x73, 0x79, 0x6C, 0x76, 0x61, 0x6E, 0x69, 0x61, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x0E, 0x49, 0x53, 0x43,
        0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x69, 0x6E, 0x67, 0x31, 0x1C, 0x30, 0x1A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x6E, 0x6F, 0x63,
        0x34, 0x2E, 0x64, 0x63, 0x63, 0x73, 0x2E, 0x75, 0x70, 0x65, 0x6E, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86,
        0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xB8, 0xB0, 0x75, 0xC8, 0xD3,
        0xE0, 0xC2, 0x03, 0x9A, 0x63, 0x0C, 0xB5, 0xBE, 0x83, 0x91, 0x8F, 0xF6, 0x1E, 0xC4, 0xCB, 0xAF, 0xA1, 0x73, 0xC8, 0xC9, 0x67, 0x54, 0x77, 0xFE, 0xC0,
        0x67, 0x6D, 0x61, 0x68, 0xEA, 0x01, 0xA9, 0xC7, 0xA2, 0xF8, 0x47, 0xCD, 0xFD, 0x05, 0x1D, 0xB0, 0x97, 0x38, 0xBA, 0xAF, 0x78, 0xA4, 0x28, 0xA8, 0xFE,
        0x86, 0xF3, 0x40, 0x5F, 0xA7, 0x7D, 0xA7, 0x74, 0x88, 0xBD, 0x58, 0xE1, 0xE2, 0x94, 0x89, 0x80, 0x86, 0xF0, 0x2B, 0x28, 0x98, 0x08, 0x27, 0x78, 0xB1,
        0x18, 0x65, 0xF0, 0x01, 0x64, 0x8C, 0x78, 0x06, 0xC1, 0x46, 0x27, 0xE8, 0x43, 0xA8, 0x2E, 0xA8, 0x00, 0x98, 0xFD, 0x35, 0x53, 0x26, 0xF5, 0xDC, 0x75,
        0x3F, 0x7C, 0x7B, 0xE3, 0x18, 0x06, 0x5F, 0x5A, 0x16, 0x75, 0xF8, 0x9E, 0xD5, 0x58, 0x93, 0x86, 0x2B, 0x9A, 0x5A, 0xD5, 0xC0, 0x1C, 0x79, 0x02, 0x03,
        0x01, 0x00, 0x01, 0xA3, 0x81, 0xA6, 0x30, 0x81, 0xA3, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2B, 0x06, 0x01,
        0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x40, 0x06, 0x03, 0x55, 0x1D, 0x1F, 0x04, 0x39, 0x30,
        0x37, 0x30, 0x35, 0xA0, 0x33, 0xA0, 0x31, 0x86, 0x2F, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x63, 0x72, 0x6C, 0x2E, 0x74, 0x68, 0x61, 0x77, 0x74,
        0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
        0x43, 0x41, 0x2E, 0x63, 0x72, 0x6C, 0x30, 0x32, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06,
        0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6F, 0x63, 0x73, 0x70, 0x2E, 0x74, 0x68,
        0x61, 0x77, 0x74, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0D, 0x06,
        0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x1E, 0x05, 0xB8, 0xBC, 0x4B, 0x2F, 0xF8, 0x41, 0x50,
        0x78, 0x2F, 0xFA, 0x97, 0xFB, 0x71, 0xC0, 0xAB, 0x49, 0x53, 0xC3, 0xD7, 0x64, 0x47, 0xE2, 0xD6, 0x64, 0xC1, 0x41, 0x72, 0x66, 0xFC, 0x95, 0x22, 0x6B,
        0x55, 0x87, 0x83, 0xCF, 0x75, 0x1A, 0x5B, 0x1E, 0x2E, 0x93, 0x6D, 0x49, 0xFF, 0x30, 0xC1, 0x9A, 0x88, 0xA3, 0x68, 0x7E, 0xC7, 0xAD, 0x7B, 0x50, 0xBE,
        0xEF, 0x05, 0x90, 0xB4, 0x9D, 0xA0, 0xA5, 0xCC, 0x50, 0x8F, 0x3B, 0xBF, 0x68, 0x03, 0x82, 0x85, 0x77, 0xFE, 0x53, 0xBD, 0x19, 0x5A, 0x02, 0x23, 0x02,
        0xC6, 0x9B, 0x66, 0xE0, 0x34, 0xCE, 0xA4, 0xB9, 0x34, 0x0A, 0x82, 0xD5, 0x58, 0x48, 0x55, 0x04, 0x47, 0x31, 0x4C, 0x03, 0x49, 0x7C, 0x40, 0x50, 0x37,
        0x73, 0x45, 0x38, 0x9F, 0xB4, 0xF4, 0x1D, 0xC0, 0xBD, 0x4D, 0xCC, 0x6E, 0x97, 0x61, 0x6D, 0xF1, 0x08, 0x34, 0xCD
	};
	
	// Here is where you need to put the certificate references from above
	NSArray * certificates = [NSArray arrayWithObjects:
		[NSData dataWithBytesNoCopy:&noc2CertificateRAW length:sizeof(noc2CertificateRAW) freeWhenDone:NO], // SETTING HERE
		[NSData dataWithBytesNoCopy:&noc4CertificateRAW length:sizeof(noc4CertificateRAW) freeWhenDone:NO], // SETTING HERE
		nil
	];

#pragma mark Get the interfaces 
	
	NSString * airtportPortName = nil;
	NSString * ethernetMACAddress = nil;
	NSString * airportMACAddress= nil;
	
// for testing

//airtportPortName = @"en1";
//airportMACAddress = @"01:01:01:01:01:01";

//	

	// this next part is to work arround a bug in 10.5 (early)... 
	//	don't really know why this works, but a bit of trial-and error tells me it does
	const char * interfaceTypes[] = { kIO80211InterfaceClass, kIOEthernetInterfaceClass };
	
	CFMutableDictionaryRef matchingDict;
	for (int i = 0; i < 2; i++) {

		io_iterator_t matchingServices;
		matchingDict = IOServiceMatching(interfaceTypes[i]);

		if (KERN_SUCCESS != IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &matchingServices)) {
			ERROR(@"Internal Error", 2, fatalForBoth)
		}
		
		io_object_t thisNetworkPort;	

		while(thisNetworkPort = IOIteratorNext(matchingServices)) {
			
			NSString * bsdName = (NSString *)IORegistryEntryCreateCFProperty(thisNetworkPort, (CFStringRef)@"BSD Name", NULL, kNilOptions);
	
			// there is a bug that is documented that requires us to look up the parrent (conrtoller) to see if this is an arport card
			//	this is all from MoreSCF in the Apple example code
			io_service_t controller;
			if (KERN_SUCCESS == IORegistryEntryGetParentEntry(thisNetworkPort, kIOServicePlane, &controller)) {
				
				CFTypeRef macAddressData = IORegistryEntryCreateCFProperty(controller, CFSTR(kIOMACAddress),kCFAllocatorDefault, 0);
				UInt8 macAddress[kIOEthernetAddressSize];
				CFDataGetBytes(macAddressData, CFRangeMake(0, kIOEthernetAddressSize), macAddress);
				CFRelease(macAddressData);			

				// this is our default interface, so the one we should get the MAC address from
				if(kCFBooleanTrue == (CFBooleanRef)IORegistryEntryCreateCFProperty(thisNetworkPort, CFSTR(kIOPrimaryInterface), NULL, kNilOptions)) {
					char thisAddress[kIOEthernetAddressSize + 1];
					sprintf(thisAddress, "%02x%02x%02x%02x%02x%02x", macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]);
					ethernetMACAddress = [NSMutableString stringWithCString:thisAddress];
				}
					
				// this is our AirportCard... note that only the last one will count
				if (IOObjectConformsTo(controller, "IO80211Controller") || IOObjectConformsTo(controller, "AirPortPCI")	|| IOObjectConformsTo(controller, "AirPortDriver")) {
					airtportPortName = bsdName;
					
					char thisAddress[kIOEthernetAddressSize + 1];
					sprintf(thisAddress, "%02x:%02x:%02x:%02x:%02x:%02x", macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]);
					airportMACAddress = [NSMutableString stringWithCString:thisAddress];
				}
				
				io_name_t className;
				IOObjectGetClass(controller, className );				
				
			} else {
				ERROR(@"Internal Error", 3, fatalForBoth)
			}
			IOObjectRelease(thisNetworkPort);
			IOObjectRelease(controller);
		}
		sleep(.1); // fix a bug in getting the IOServiceMatching to oquickly
		
	}
	
	if (airtportPortName == nil) { ERROR(@"This computer does not seem to have an AirPort Card!", 4, fatalForBoth) }
	
	if (ethernetMACAddress == nil) { ERROR(@"This computer does not seem to have a main Ethernet address!", 5, fatalForBoth) }

#pragma mark -
#pragma mark Start of the big loop
	// here we are going to loop for the users
	//	we will change the process to have an efective UID of the users in order to properly act as them
	
#pragma mark - Set/reset the euid
	
	int orriginalUID = getuid();
	
	for (int userCounter = 0; userCounter < numberOfUsers; userCounter++) {
		
		struct userData thisUser = theUsers[userCounter];
		
		printf("\nWorking on user %s (uid: %d)\n", [thisUser.name cString], thisUser.uid);
		
		if (setuid(orriginalUID) == -1) { ERROR(@"Unable to reset uid!", 1, fatalForBoth) }
		if (seteuid(thisUser.uid) == -1) { ERROR([@"Unable to set userid for: " stringByAppendingString:thisUser.name], 1, fatalForBoth) }

		setenv("HOME", [thisUser.homeDirectory cString], 1); // this is needed for getting the proper keychain
		
#pragma mark -

#pragma mark Setting InternetConnect preferences

#pragma mark - Getting the preference file
	
		// this will get the path to the preferences file we need	
		NSString * pathToICPreferenceFile = [[[[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/ByHost/com.apple.networkConnect." ] stringByAppendingString:ethernetMACAddress] stringByAppendingString:@".plist"] stringByExpandingTildeInPath];
		
		NSMutableDictionary * networkConnectPreferences;
		
		if ([myFileManager fileExistsAtPath:pathToICPreferenceFile] == YES) {
			if ([myFileManager isWritableFileAtPath:pathToICPreferenceFile] == YES && [myFileManager isReadableFileAtPath:pathToICPreferenceFile] == YES) {
				// this should be the usual case, where the user already has a networkConnect preference file in place
				networkConnectPreferences = [NSMutableDictionary dictionaryWithContentsOfFile:pathToICPreferenceFile];
				
				if (networkConnectPreferences == nil) {
					ERROR([@"The preferences file is probably corrupt: " stringByAppendingString:pathToICPreferenceFile], 6, fatalForPreLeopard)
				}
				
			} else { ERROR([@"Unable to read or write preference file: " stringByAppendingString:pathToICPreferenceFile], 7, fatalForPreLeopard) }
		} else {
			if ([myFileManager isWritableFileAtPath:[[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/ByHost/"] stringByExpandingTildeInPath]] == YES) {
				// here there is no file currently, but we can write to the proper directory
				//	so we are going to create an empty dictionary, and will write this to the proper place later
				networkConnectPreferences = [NSMutableDictionary dictionaryWithCapacity:2];
			}
		}
		
		// at this point we should have an NSMutableDictionary that we can start grooming
		NSObject * genericObject; // these two are so that I can walk up the tree
		
		NSMutableArray * oldListOfICSettings;
		NSMutableArray * newListOfSettings = [NSMutableArray arrayWithCapacity:1];
		
		genericObject = [networkConnectPreferences objectForKey:@"-2"];
		if (genericObject == nil) {
			oldListOfICSettings = nil;
		} else {
			if ([genericObject isKindOfClass:[NSArray class]] == NO) {
				ERROR([@"The preferences file is probably corrupt at '-2': " stringByAppendingString: pathToICPreferenceFile], 8, fatalForPreLeopard)
			} else {
				// everything is good here
				oldListOfICSettings = (NSMutableArray *) genericObject;
			}
		}
		
		// we are now going to loop over the items and bring over everything that is not one of our target networks
		//	login iformation will be brought from the last entry found, this is a best-effort approach, and will munge it in some cases
		//	note that the passwords are stored in the keychain by UUID, and so if there is a login name we bring the UUID along for the ride
			
		NSEnumerator * myEnum = [oldListOfICSettings objectEnumerator];
		NSDictionary * testItem;
		NSDictionary * targetNetworksToDisplayName = [NSDictionary dictionaryWithObjectsAndKeys: @"AirPennNet 802.1X", @"AirPennNet", @"AirSAS 802.1X", @"airsas", nil]; // SETTING HERE
		NSDictionary * targetNetworksToOuterName = [NSDictionary dictionaryWithObjectsAndKeys: @"", @"AirPennNet", @"anonymous", @"airsas", nil]; // SETTING HERE
		NSMutableDictionary * uniqueICIDs = [NSMutableDictionary dictionaryWithCapacity:[targetNetworksToDisplayName count]];
		NSMutableDictionary * storedLogins = [NSMutableDictionary dictionaryWithCapacity:[targetNetworksToDisplayName count]];
		// this is the list of networks we are going to install
		//	IMPORTANT NOTE: regardless of how you are going to set it, make these in all lower-case!

		while (testItem = [myEnum nextObject]) {
			if([testItem objectForKey:@"Wireless Network"] != nil) {
				[uniqueICIDs setObject:@"" forKey:[(NSString *)[testItem objectForKey:@"Wireless Network"] lowercaseString]];
				[storedLogins setObject:@"" forKey:[(NSString *)[testItem objectForKey:@"Wireless Network"] lowercaseString]];
				
				if ([targetNetworksToDisplayName objectForKey:[(NSString *)[testItem objectForKey:@"Wireless Network"] lowercaseString]] == nil) {
					// the target networks we will be replacinging, so we will not bring them over
					[newListOfSettings addObject:testItem];
				} else {
					if ([testItem objectForKey:@"PPP"] != nil && [(NSString *)[(NSDictionary * )[testItem objectForKey:@"PPP"] objectForKey:@"AuthName"] isEqualToString:@""] == FALSE) {
						// here we had a setup, and so we should keep the name and the UUID
						
						[storedLogins setObject:(NSString *)[(NSDictionary * )[testItem objectForKey:@"PPP"] objectForKey:@"AuthName"] forKey:[(NSString *)[testItem objectForKey:@"Wireless Network"] lowercaseString]];
						[uniqueICIDs setObject:(NSString *)[testItem objectForKey:@"UniqueIdentifier"] forKey:[(NSString *)[testItem objectForKey:@"Wireless Network"] lowercaseString] ];
					}
				}
			}
		}
	
		// now we have to inject UUID's for the accounts that don't have them
		myEnum = [uniqueICIDs keyEnumerator];
		NSString * thisKey;
		while (thisKey = [myEnum nextObject]) {
			if ([[uniqueICIDs objectForKey:thisKey] isEqualToString:@""]) {
				[uniqueICIDs setObject:(NSString *)CFUUIDCreateString(kCFAllocatorDefault, CFUUIDCreate(kCFAllocatorDefault)) forKey:thisKey];
			}
		}

		
		// now we create the new settings we want in there	
#pragma mark - InternetConnect settings
		NSDictionary * airPennNetIC = [NSDictionary dictionaryWithObjectsAndKeys: // SETTING HERE
			[NSNumber numberWithBool:TRUE], @"ConnectByDefault",
			[NSDictionary dictionary], @"IPSec",
			@"en1", @"Network Port", // this should get improved
			[NSDictionary dictionaryWithObjectsAndKeys: [storedLogins objectForKey:@"airpennnet"], @"AuthName", @"", @"AuthPassword", @"AirPennNet 802.1X", @"UserDefinedName", nil], @"PPP",
			[NSArray arrayWithObjects:
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], @"On", @"TTLS", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"TLS", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"EAP-FAST", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"PEAP", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"LEAP", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"MD5", @"Protocol", nil],
			nil], @"Protocols",
			@"PAP", @"TTLSInnerAuthentication",
			[uniqueICIDs objectForKey:@"airpennnet"], @"UniqueIdentifier",
			@"AirPennNet", @"Wireless Network",
		nil];
		[newListOfSettings addObject:airPennNetIC];
		
		NSDictionary * airPennSasIC = [NSDictionary dictionaryWithObjectsAndKeys: // SETTING HERE
			[NSNumber numberWithBool:TRUE], @"ConnectByDefault",
			[NSDictionary dictionary], @"IPSec",
			@"en1", @"Network Port", // this should get improved
			@"anonymous", @"OuterIdentity", // this should get improved
			[NSDictionary dictionaryWithObjectsAndKeys: [storedLogins objectForKey:@"airsas"], @"AuthName", @"", @"AuthPassword", @"AirSAS 802.1X", @"UserDefinedName", nil], @"PPP",
			[NSArray arrayWithObjects:
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], @"On", @"TTLS", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"TLS", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"EAP-FAST", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"PEAP", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"LEAP", @"Protocol", nil],
				[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"On", @"MD5", @"Protocol", nil],
				nil
			], @"Protocols",
			@"PAP", @"TTLSInnerAuthentication",
			[uniqueICIDs objectForKey:@"airsas"], @"UniqueIdentifier",
			@"airsas", @"Wireless Network",
		nil];
		[newListOfSettings addObject:airPennSasIC];
		
		[networkConnectPreferences setObject:newListOfSettings forKey:@"-2"];

		// now we have to do almost all the same stuff for Leopard
		
		// first we will handle getting the data out of the current preference file
		
#pragma mark - EAP Binding Preferences (Leopard)
		
		NSString * pathToEAPBindingsFile = [[[[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/ByHost/com.apple.eap.bindings." ] stringByAppendingString:ethernetMACAddress] stringByAppendingString:@".plist"] stringByExpandingTildeInPath];
		NSMutableDictionary * eapBindings;
		if ([myFileManager fileExistsAtPath:pathToEAPBindingsFile] == YES) {
			if ([myFileManager isWritableFileAtPath:pathToEAPBindingsFile] == YES && [myFileManager isReadableFileAtPath:pathToEAPBindingsFile] == YES) {
				// this should be the usual case, where the user already has a networkConnect preference file in place
				eapBindings = [NSMutableDictionary dictionaryWithContentsOfFile:pathToEAPBindingsFile];
				
				if (eapBindings == nil) {
					ERROR([@"The preferences file is probably corrupt: " stringByAppendingString:pathToEAPBindingsFile], 9, fatalForLeopard)
				}
			} else {
				ERROR([@"Unable to read or write preference file at: " stringByAppendingString:pathToEAPBindingsFile], 10, fatalForLeopard)
			}
		} else {
			if ([myFileManager isWritableFileAtPath:[[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/ByHost"] stringByExpandingTildeInPath]] == YES) {
				// here there is no file currently, but we can write to the proper directory
				//	so we are going to create an empty dictionary, and will write this to the proper place later
				eapBindings = [NSMutableDictionary dictionaryWithCapacity:1];
			} else {
				ERROR([@"Unable to create new preference file at: " stringByAppendingString:pathToEAPBindingsFile], 11, fatalForNeither)
			}
		}
		
		
		NSArray * oldEAPBindings = [eapBindings objectForKey:airportMACAddress];
		NSMutableArray * newEAPBindings = [NSMutableArray arrayWithCapacity:[targetNetworksToDisplayName count]];
		NSMutableDictionary * uniqueEapIDsForNetwork = [NSMutableDictionary dictionaryWithCapacity:1]; // we can bring them over appropriately
		// we don't care if this is nil, as we will be replacing it anyways
		//	so lets mine it for data
		myEnum = [oldEAPBindings objectEnumerator];
		while (testItem = [myEnum nextObject]) {
			if ([testItem objectForKey:@"Wireless Network"] == nil) {
				// we are just going to let this one go
			} else {
				if ([targetNetworksToDisplayName objectForKey:[[testItem objectForKey:@"Wireless Network"] lowercaseString]] == nil) {
					// not one of ours, so it gets a free pass
					[newEAPBindings addObject:testItem]; [testItem retain];
				} else {
					// this is one of ours, so we grab the information out of it, and the let it go
					[uniqueEapIDsForNetwork setObject:[[testItem objectForKey:@"UniqueIdentifier"] lowercaseString] forKey:[[testItem objectForKey:@"Wireless Network"] lowercaseString]];
				}
			}
		}

		myEnum = [targetNetworksToDisplayName keyEnumerator];
		NSString * thisNetwork;
		while (thisNetwork = [myEnum nextObject]) {
			if ([uniqueEapIDsForNetwork objectForKey:thisNetwork] == nil || [[uniqueEapIDsForNetwork objectForKey:thisNetwork] isEqualToString:@""]) {
				// before creating a new UUID, we will fall back and see if the pre-10.5 settings has something for this
				if ([uniqueICIDs objectForKey:thisNetwork] == nil) {
					[uniqueEapIDsForNetwork setObject:(NSString *)CFUUIDCreateString(kCFAllocatorDefault, CFUUIDCreate(kCFAllocatorDefault)) forKey:thisNetwork];
				} else {
					[uniqueEapIDsForNetwork setObject:[uniqueICIDs objectForKey:thisNetwork] forKey:thisNetwork];
				}
			}
			[newEAPBindings addObject:[NSDictionary dictionaryWithObjectsAndKeys:[uniqueEapIDsForNetwork objectForKey:thisNetwork], @"UniqueIdentifier", thisNetwork, @"Wireless Network", nil]];
		}

		[eapBindings setObject:newEAPBindings forKey:airportMACAddress];

#pragma mark - EAP Preferences (Leopard)
		
		NSString * pathToEAPPreferenceFile = [[[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/com.apple.eap.profiles" ] stringByAppendingString:@".plist"] stringByExpandingTildeInPath];

		NSMutableDictionary * eapProfilePreferences;
		if ([myFileManager fileExistsAtPath:pathToEAPPreferenceFile] == YES) {
			if ([myFileManager isWritableFileAtPath:pathToEAPPreferenceFile] == YES && [myFileManager isReadableFileAtPath:pathToEAPPreferenceFile] == YES) {
				// this should be the usual case, where the user already has a networkConnect preference file in place
				eapProfilePreferences = [NSMutableDictionary dictionaryWithContentsOfFile:pathToEAPPreferenceFile];
				
				if (eapProfilePreferences == nil) {
					ERROR([@"The preferences file is probably corrupt: " stringByAppendingString:pathToEAPPreferenceFile], 12, fatalForLeopard)
				}
			} else {
				ERROR([@"Unable to read or write preference file: " stringByAppendingString:pathToEAPPreferenceFile], 13, fatalForLeopard)
			}
		} else {
			if ([myFileManager isWritableFileAtPath:[[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/"] stringByExpandingTildeInPath]] == YES) {
				// here there is no file currently, but we can write to the proper directory
				//	so we are going to create an empty dictionary, and will write this to the proper place later
				eapProfilePreferences = [NSMutableDictionary dictionaryWithCapacity:2];
			} else {
				ERROR([@"Unable to create new preference file: " stringByAppendingString:pathToEAPPreferenceFile], 14, fatalForLeopard)
			}
		}
		
		NSArray * oldProfilesObject;
		NSMutableArray * newEAPProfilesObject = [NSMutableArray arrayWithCapacity:[targetNetworksToDisplayName count]];
		genericObject = [eapProfilePreferences objectForKey:@"Profiles"];
		if (genericObject == nil) {
			[eapProfilePreferences setObject:[NSMutableArray arrayWithCapacity:1] forKey:@"Profiles"];
			oldProfilesObject = [eapProfilePreferences objectForKey:@"Profiles"];
		} else {
			if ([genericObject isKindOfClass:[NSArray class]] == NO) {
				ERROR([@"The preferences file is probably corrupt at 'Profiles':  " stringByAppendingString:pathToICPreferenceFile], 15, fatalForLeopard)
			} else {
				// everything is good here
				oldProfilesObject = (NSMutableArray *) genericObject;
			}		
		}
		
		NSMutableDictionary * eapUIDsToUsernames = [NSMutableDictionary dictionaryWithCapacity:[targetNetworksToDisplayName count]];
		myEnum = [oldProfilesObject objectEnumerator];
		while (genericObject = [myEnum nextObject]) {
			if (genericObject == nil || [genericObject isKindOfClass:[NSDictionary class]] == NO) {
				// these are just not going to move on
			} else {
				NSArray * netWorks = [uniqueEapIDsForNetwork allKeysForObject:[(NSDictionary *)genericObject objectForKey:@"UniqueIdentifier"]];
				if (netWorks == nil) {
					// this is not one of the items we are managing, so it continues on
					[newEAPProfilesObject addObject:(NSDictionary *)genericObject];
				} else {
					// we are managing this one, so we will just need to harvest the userName
					if (
						[(NSDictionary *)genericObject objectForKey:@"EAPClientConfiguration"] != nil &&
						[(NSDictionary *)[(NSDictionary *)genericObject objectForKey:@"EAPClientConfiguration"] objectForKey:@"UserName"] != nil &&
						[(NSString *)[(NSDictionary *)[(NSDictionary *)genericObject objectForKey:@"EAPClientConfiguration"] objectForKey:@"UserName"] isEqualToString:@""] != TRUE
					) {
						[eapUIDsToUsernames setObject:[(NSDictionary *)[(NSDictionary *)genericObject objectForKey:@"EAPClientConfiguration"] objectForKey:@"UserName"] forKey:(NSDictionary *)genericObject];
					}
				}
			}
		}
		// now we add in the records we need
		myEnum = [targetNetworksToDisplayName keyEnumerator];	
		while (thisNetwork = [myEnum nextObject]) {
			[newEAPProfilesObject addObject:[NSDictionary dictionaryWithObjectsAndKeys:
				[NSNumber numberWithBool:TRUE], @"ConnectByDefault",
				[NSDictionary dictionaryWithObjectsAndKeys:
					[NSArray arrayWithObject:[NSNumber numberWithInt:21]], @"AcceptEAPTypes",
					[targetNetworksToOuterName objectForKey:thisNetwork], @"OuterIdentity",
					@"PAP", @"TTLSInnerAuthentication",
					[eapUIDsToUsernames objectForKey:[uniqueEapIDsForNetwork objectForKey:thisNetwork]], @"UserName", 
					nil
				], @"EAPClientConfiguration",
				[uniqueEapIDsForNetwork objectForKey:thisNetwork], @"UniqueIdentifier",
				[targetNetworksToDisplayName objectForKey:thisNetwork], @"UserDefinedName",
				nil
			]];
		}
		
		[eapProfilePreferences setObject:newEAPProfilesObject forKey:@"Profiles"];
		
#pragma mark - Write out preferences
		
		// this next line is for testing
		// pathToPreferenceFile = [[@"/tmp/testme-" stringByAppendingString:theMACAddress] stringByAppendingString:@".plist"];
		
		// here we are going to set the preference to make sure it shows up in InternertConnect
		NSString * pathToICPref = [[thisUser.homeDirectory stringByAppendingString:@"/Library/Preferences/com.apple.internetconnect.plist"] stringByExpandingTildeInPath];
		NSMutableDictionary * interfacePreferences = [NSMutableDictionary dictionaryWithContentsOfFile:pathToICPref];
		if (interfacePreferences != nil) {
			[interfacePreferences setObject:[NSNumber numberWithBool:TRUE] forKey:@"802.1X"];
		} else {
			interfacePreferences = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:TRUE] forKey:@"802.1X"];
		}
		if ([interfacePreferences writeToFile:pathToICPref atomically:TRUE] == FALSE) {
			/// note that we are not going to let this stop us
			ERROR([@"Unable to write preference file: " stringByAppendingString:pathToICPref], 16, fatalForNeither)
		} else {
			printf("Wrote new preference file to: %s\n", [pathToICPref cString]);
		}
		
		// the networkConnect preference
		if([networkConnectPreferences writeToFile:pathToICPreferenceFile atomically:TRUE] == NO) {
			ERROR([@"Unable to write preference file: " stringByAppendingString:pathToICPreferenceFile], 17, fatalForPreLeopard)
		} else {
			printf("Wrote new preference file to: %s\n", [pathToICPreferenceFile cString]);
		}
		
		// the EAP Binding preference
		if([eapBindings writeToFile:pathToEAPBindingsFile atomically:TRUE] == NO) {
			ERROR([@"Unable to write preference file: " stringByAppendingString:pathToEAPBindingsFile], 18, fatalForLeopard)
		} else {
			printf("Wrote new preference file to: %s\n", [pathToEAPBindingsFile cString]);
		}
		
		// the EAP preference
		if([eapProfilePreferences writeToFile:pathToEAPPreferenceFile atomically:TRUE] == NO) {
			ERROR([@"Unable to write preference file: " stringByAppendingString:pathToEAPPreferenceFile], 19, fatalForLeopard)
		} else {
			printf("Wrote new preference file to: %s\n", [pathToEAPPreferenceFile cString]);
		}
		
#pragma mark -
#pragma mark Start of the Loop

		SecKeychainRef defaultKeychain;

		//NSString * pathToKeychain = [thisUser.homeDirectory stringByAppendingString:@"/Library/Keychains/login.keychain"];
		//if(noErr != SecKeychainOpen([pathToKeychain cString], &defaultKeychain)) {
		if (noErr != SecKeychainCopyDefault (&defaultKeychain)) { // we have set the ENV variable, so this should pick up coorectly
			ERROR(@"Unable to get Keychain!", 22, fatalForBoth)
		}

		NSData * myData;
		NSEnumerator * certificateEnumerator = [certificates objectEnumerator];
		while (myData = [certificateEnumerator nextObject]) {
			
			NSString * certificateName;
			
			SecCertificateRef embededCertificate;
			NSCalendarDate * embededCertificateDate;

			SecCertificateRef keychainCertificate;
			NSCalendarDate * keychainCertificateDate;
			NSString * keychainCertificateName; // this is going to be identical, but I am getting it anyways

#pragma mark - Get Embeded Certificate Data
			
			CSSM_DATA myCSSMData;
			myCSSMData.Data = (uint8 *)[myData bytes];
			myCSSMData.Length = [myData length];
				
			if (noErr == SecCertificateCreateFromData (
					&myCSSMData,
					CSSM_CERT_X_509v3, // default
					CSSM_CERT_ENCODING_BER, // I am unsure of the encoding, so defaulting
					&embededCertificate
				)) {
				
				getDateAndNameOfCertificate ( embededCertificate, &embededCertificateDate, &certificateName);
			}
			
			printf("%s:\n", [certificateName cString]);

// Here we look in the keychain
#pragma mark - Search Keychain
			
			SecKeychainAttribute myAttribute;
			myAttribute.data = (uint8 *)[certificateName cString];
			myAttribute.length = strlen([certificateName cString]);
			
			SecKeychainSearchRef mySearch;		
			
			myAttribute.tag = kSecLabelItemAttr; // this gets the label, I am not sure that this is the best target
			SecKeychainAttributeList myAttrList;
			myAttrList.count = 1;
			myAttrList.attr = &myAttribute;
			
			if (noErr == SecKeychainSearchCreateFromAttributes (
				defaultKeychain, // current users Keychain
				kSecCertificateItemClass, // a certificate
				&myAttrList,
				&mySearch
			)) {
				// here we have a keychain search, and need to find entries
				//	since I think that the 802.1x client will go for the first one it finds, so will we
				
				SecKeychainItemRef keychainItem; // this stores the item we have back from the keychain, so we can re-cast it
				
				if ( noErr == SecKeychainSearchCopyNext(mySearch, &keychainItem) ) {
					// here we have a keychain item, and just need to re-cast it and set the policy
					
					keychainCertificate = (SecCertificateRef) keychainItem;
					
					getDateAndNameOfCertificate ( (SecCertificateRef)keychainItem, &keychainCertificateDate, &keychainCertificateName);
					
				} else {
					// there was no such item in the Keychain
					
					keychainCertificate = nil;
					keychainCertificateDate = nil;
					keychainCertificateName = nil;
				}
				
				CFRelease(mySearch);
				
			} else {
				ERROR(@"Could not search keychain!", 20, fatalForBoth)
			}
			
#pragma mark - Compare Dates of Certificates
			// here is where we are going to put the certificate we are going to be using:
			SecCertificateRef certificateToUse;
		
			if (keychainCertificate == nil || [keychainCertificateDate compare:embededCertificateDate] == NSOrderedAscending) {
				
				if (keychainCertificate != nil) {
					// here we have to remove the old certificate
					if (noErr == SecKeychainItemDelete((SecKeychainItemRef)keychainCertificate)) {
						// 
						printf("\tRemoved old Certificate\n");
						
					} else {
						ERROR([@"Could not delete Certificate: " stringByAppendingString:certificateName], 21, fatalForBoth)
					}
				}
				
// This section pushes the certificate into the keychain
#pragma mark - Push into Keychain
				
				SecExternalFormat certificateFormat = kSecFormatX509Cert;
				SecExternalItemType theType = kSecItemTypeCertificate;
				
				CFArrayRef theItems;
				
				OSStatus whateverStatus;
				whateverStatus = SecKeychainItemImport (
					(CFDataRef) myData,
					(CFStringRef) embededCertificate,
					&certificateFormat,
					&theType,
					0, // as documented by Apple
					NULL, // we don't have keyparamaters
					defaultKeychain,
					&theItems
				);
				
				if(noErr == whateverStatus) {
					printf("\tAdding New Certificate\n");
				} else if (errSecDuplicateItem == whateverStatus) {
					// actually, we should never get here...
					ERROR([@"Certificate already in Keychain: " stringByAppendingString:certificateName], 23, fatalForBoth)
					
				} else {
					ERROR([[[@"Failed to add Certificate: " stringByAppendingString:certificateName] stringByAppendingString:@" certificate error: "] stringByAppendingString:[[NSNumber  numberWithInt:whateverStatus] stringValue]], 24, fatalForBoth)
				}
				
				certificateToUse = (SecCertificateRef)[(NSArray *)theItems objectAtIndex:0];
						
			} else {
				// This means that they are the same, or we already have a newer one in the Keychain
				//	ie... nothing to do here
				
				certificateToUse = keychainCertificate;
				printf("\tNewest certificate was already in Keycahin\n");

			}
			
// now we set the trust settings
#pragma mark - Set Trust Settings				
			// this has to be re-created every time as one of the releases implicitly covers it
			SecPolicySearchRef myPolicySearch;
			if (noErr != SecPolicySearchCreate (
					CSSM_CERT_X_509v3, // I am unsure, so this is the one recomenneded
					&CSSMOID_APPLE_TP_EAP, // this is the security setting that 802.1X uses
					NULL,
					&myPolicySearch
					)
				) {
				ERROR(@"Could not create keychain policy search!", 25, fatalForBoth)
			}
			
			SecPolicyRef myPolicy;
			
			if (noErr == SecPolicySearchCopyNext(myPolicySearch, &myPolicy)) {
				
				SecTrustResultType testTrustSetting;
				SecTrustRef trustRef;
				
				if (noErr == SecTrustCreateWithCertificates (
					(SecCertificateRef)certificateToUse, // this works despite the warning, this is as documented
					myPolicy,
					&trustRef
				)) {
					
					OSStatus myStatus;// = SecTrustSetKeychains (trustRef, defaultKeychain);
					
					SecKeychainRef testMe;
					myStatus = SecKeychainCopyDefault (&testMe);
										
					
					// another point for failure
					if (noErr == SecTrustEvaluate (trustRef, &testTrustSetting)) {
						// we should die here
						
						if (testTrustSetting != kSecTrustResultProceed) {
							
							OSStatus myStats = SecTrustSetUserTrust(certificateToUse, myPolicy, kSecTrustResultProceed);
							if (myStats == noErr) {
								// at this point we should have made the change to the policy
								printf("\tSucessfully set policy\n");
							} else if (myStats == errSecAuthFailed) {
								ERROR(@"The user failed authentication!", 27, fatalForNeither)
							} else if (myStats == errAuthorizationCanceled) {
								ERROR(@"The user failed authentication!", 29, fatalForNeither)
							} else if (myStats == errAuthorizationInteractionNotAllowed) {
								ERROR(@"It looks like this is running from a headless installer!", 30, fatalForBoth)
							} else {
								ERROR([@"Unknown Policy error code: " stringByAppendingString:[[NSNumber numberWithInt:myStats] stringValue]], 31, fatalForBoth)
							}
							
						} else {
							printf("\tPolicy was already set\n");
						}
					} else {
						// unabel to evaluate certificate
					}
					
					CFRelease(trustRef);
				} else {
					// unable to get trust policy to get to compare
				}
							
				CFRelease(myPolicy);

			} else {
				// somehow there was not a policy object
				ERROR([@"could not find policy for certificate: " stringByAppendingString:certificateName], 32, fatalForBoth)
			}
			
			CFRelease(myPolicySearch);
			if (keychainCertificate != nil) {
				CFRelease(keychainCertificate);
			}
			CFRelease(embededCertificate); // this one should always exist
			CFRelease(defaultKeychain);

#pragma mark End of Loop
		}

#pragma mark -
#pragma mark End of the big loop
		printf("\n");
	}

	
    [pool release];

	return 0;
	// everything is golden
}